{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Run MLFlow Model in Seldon Core\n",
    "\n",
    "This notebook shows how you can easily train a model using [MLFlow](https://mlflow.org/) and serve requests within Seldon Core on Kubernetes.\n",
    "\n",
    "Dependencies\n",
    "\n",
    "  * ```pip install seldon-core```\n",
    "  * ```pip install mlflow```\n",
    "  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train Example MlFlow Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cloning into 'mlflow'...\n",
      "remote: Enumerating objects: 14, done.\u001b[K\n",
      "remote: Counting objects: 100% (14/14), done.\u001b[K\n",
      "remote: Compressing objects: 100% (11/11), done.\u001b[K\n",
      "remote: Total 7786 (delta 3), reused 4 (delta 1), pack-reused 7772\u001b[K\n",
      "Receiving objects: 100% (7786/7786), 8.15 MiB | 407.00 KiB/s, done.\n",
      "Resolving deltas: 100% (4781/4781), done.\n",
      "Checking connectivity... done.\n"
     ]
    }
   ],
   "source": [
    "!git clone https://github.com/mlflow/mlflow"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Elasticnet model (alpha=0.500000, l1_ratio=0.500000):\r\n",
      "  RMSE: 0.82224284975954\r\n",
      "  MAE: 0.6278761410160691\r\n",
      "  R2: 0.12678721972772689\r\n"
     ]
    }
   ],
   "source": [
    "!python mlflow/examples/sklearn_elasticnet_wine/train.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test Inference Locally"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[34mfrom\u001b[39;49;00m \u001b[04m\u001b[36mmlflow\u001b[39;49;00m \u001b[34mimport\u001b[39;49;00m pyfunc\r\n",
      "\u001b[34mimport\u001b[39;49;00m \u001b[04m\u001b[36mos\u001b[39;49;00m\r\n",
      "\u001b[34mimport\u001b[39;49;00m \u001b[04m\u001b[36mpandas\u001b[39;49;00m \u001b[34mas\u001b[39;49;00m \u001b[04m\u001b[36mpd\u001b[39;49;00m\r\n",
      "\r\n",
      "\u001b[34mclass\u001b[39;49;00m \u001b[04m\u001b[32mMyMlflowModel\u001b[39;49;00m(\u001b[36mobject\u001b[39;49;00m):\r\n",
      "\r\n",
      "    \u001b[34mdef\u001b[39;49;00m \u001b[32m__init__\u001b[39;49;00m(\u001b[36mself\u001b[39;49;00m):\r\n",
      "        \u001b[36mself\u001b[39;49;00m.pyfunc_model = pyfunc.load_pyfunc(\u001b[33m\"\u001b[39;49;00m\u001b[33mmlruns/0/\u001b[39;49;00m\u001b[33m\"\u001b[39;49;00m+\u001b[36mnext\u001b[39;49;00m(os.walk(\u001b[33m'\u001b[39;49;00m\u001b[33mmlruns/0\u001b[39;49;00m\u001b[33m'\u001b[39;49;00m))[\u001b[34m1\u001b[39;49;00m][\u001b[34m0\u001b[39;49;00m]+\u001b[33m\"\u001b[39;49;00m\u001b[33m/artifacts/model\u001b[39;49;00m\u001b[33m\"\u001b[39;49;00m)\r\n",
      "        \r\n",
      "    \u001b[34mdef\u001b[39;49;00m \u001b[32mpredict\u001b[39;49;00m(\u001b[36mself\u001b[39;49;00m,X,features_names):\r\n",
      "        \u001b[34mif\u001b[39;49;00m \u001b[35mnot\u001b[39;49;00m features_names \u001b[35mis\u001b[39;49;00m \u001b[36mNone\u001b[39;49;00m \u001b[35mand\u001b[39;49;00m \u001b[36mlen\u001b[39;49;00m(features_names)>\u001b[34m0\u001b[39;49;00m:\r\n",
      "            df = pd.DataFrame(data=X,columns=features_names)\r\n",
      "        \u001b[34melse\u001b[39;49;00m:\r\n",
      "            df = pd.DataFrame(data=X)\r\n",
      "        \u001b[34mreturn\u001b[39;49;00m \u001b[36mself\u001b[39;49;00m.pyfunc_model.predict(df)\r\n"
     ]
    }
   ],
   "source": [
    "!pygmentize MyMlflowModel.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---> Installing application source...\n",
      "---> Installing dependencies ...\n",
      "Looking in links: /whl\n",
      "Collecting mlflow (from -r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/e5/f7/d349b011f4e03b06a3a0f2916647c578cd89d338f1e244ddb4e1fee600fc/mlflow-0.9.0.1-py3-none-any.whl (11.8MB)\n",
      "Collecting sklearn (from -r requirements.txt (line 2))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/1e/7a/dbb3be0ce9bd5c8b7e3d87328e79063f8b263b2b1bfa4774cb1147bfcd3f/sklearn-0.0.tar.gz\n",
      "Collecting pandas (from -r requirements.txt (line 3))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/19/74/e50234bc82c553fecdbd566d8650801e3fe2d6d8c8d940638e3d8a7c5522/pandas-0.24.2-cp36-cp36m-manylinux1_x86_64.whl (10.1MB)\n",
      "Collecting mleap>=0.8.1 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/3b/17/486a3cf30695581f029ba618ee878252a9e9f859c48913e30a07d13c8a21/mleap-0.8.1.tar.gz\n",
      "Requirement already satisfied: click>=7.0 in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (7.0)\n",
      "Collecting gitpython>=2.1.0 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/fe/e5/fafe827507644c32d6dc553a1c435cdf882e0c28918a5bab29f7fbebfb70/GitPython-2.1.11-py2.py3-none-any.whl (448kB)\n",
      "Collecting databricks-cli>=0.8.0 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/b2/9e/e2868a5560827357796f8682803bbac73d30de4e890604e61f2488ce9c03/databricks-cli-0.8.6.tar.gz (41kB)\n",
      "Collecting querystring-parser (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/57/64/3086a9a991ff3aca7b769f5b0b51ff8445a06337ae2c58f215bcee48f527/querystring_parser-1.2.3.tar.gz\n",
      "Requirement already satisfied: Flask in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (1.0.2)\n",
      "Collecting cloudpickle (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/6e/bc/67f13115adcce4efc5e4d7f8220fb9a50aaa2b5c7ed460b26cbb76aa76ad/cloudpickle-0.8.1-py2.py3-none-any.whl\n",
      "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (1.12.0)\n",
      "Collecting entrypoints (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/ac/c6/44694103f8c221443ee6b0041f69e2740d89a25641e62fb4f2ee568f2f9c/entrypoints-0.3-py2.py3-none-any.whl\n",
      "Collecting boto3>=1.7.12 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/c3/ef/f81d8c35c7254fe2af9a1a8e034f07b88a824a441f37955d0a07a90b8ec7/boto3-1.9.130-py2.py3-none-any.whl (128kB)\n",
      "Collecting sqlparse (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/ef/53/900f7d2a54557c6a37886585a91336520e5539e3ae2423ff1102daf4f3a7/sqlparse-0.3.0-py2.py3-none-any.whl\n",
      "Requirement already satisfied: requests>=2.17.3 in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (2.21.0)\n",
      "Collecting scipy (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/7f/5f/c48860704092933bf1c4c1574a8de1ffd16bf4fde8bab190d747598844b2/scipy-1.2.1-cp36-cp36m-manylinux1_x86_64.whl (24.8MB)\n",
      "Requirement already satisfied: pyyaml in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (5.1)\n",
      "Collecting docker>=3.6.0 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/48/68/c3afca1a5aa8d2997ec3b8ee822a4d752cf85907b321f07ea86888545152/docker-3.7.2-py2.py3-none-any.whl (134kB)\n",
      "Collecting scikit-learn (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/5e/82/c0de5839d613b82bddd088599ac0bbfbbbcbd8ca470680658352d2c435bd/scikit_learn-0.20.3-cp36-cp36m-manylinux1_x86_64.whl (5.4MB)\n",
      "Collecting python-dateutil (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/41/17/c62faccbfbd163c7f57f3844689e3a78bae1f403648a6afb1d0866d87fbb/python_dateutil-2.8.0-py2.py3-none-any.whl (226kB)\n",
      "Requirement already satisfied: protobuf>=3.6.0 in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (3.7.1)\n",
      "Collecting simplejson (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/e3/24/c35fb1c1c315fc0fffe61ea00d3f88e85469004713dab488dee4f35b0aff/simplejson-3.16.0.tar.gz (81kB)\n",
      "Requirement already satisfied: numpy in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (1.16.2)\n",
      "Collecting gunicorn (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/8c/da/b8dd8deb741bff556db53902d4706774c8e1e67265f69528c14c003644e6/gunicorn-19.9.0-py2.py3-none-any.whl (112kB)\n",
      "Collecting pytz>=2011k (from pandas->-r requirements.txt (line 3))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/3d/73/fe30c2daaaa0713420d0382b16fbb761409f532c56bdcc514bf7b6262bb6/pytz-2019.1-py2.py3-none-any.whl (510kB)\n",
      "Collecting argparse>=1.1 (from mleap>=0.8.1->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/f2/94/3af39d34be01a24a6e65433d19e107099374224905f1e0cc6bbe1fd22a2f/argparse-1.4.0-py2.py3-none-any.whl\n",
      "Collecting nose-exclude>=0.5.0 (from mleap>=0.8.1->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/63/cf/90c4be56bf11b7bc8801086d9445baf731aa36b8e8fc5791731e8e604dcd/nose-exclude-0.5.0.tar.gz\n",
      "Collecting gitdb2>=2.0.0 (from gitpython>=2.1.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/da/30/a407568aa8d8f25db817cf50121a958722f3fc5f87e3a6fba1f40c0633e3/gitdb2-2.0.5-py2.py3-none-any.whl (62kB)\n",
      "Collecting tabulate>=0.7.7 (from databricks-cli>=0.8.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/c2/fd/202954b3f0eb896c53b7b6f07390851b1fd2ca84aa95880d7ae4f434c4ac/tabulate-0.8.3.tar.gz (46kB)\n",
      "Collecting configparser>=0.3.5 (from databricks-cli>=0.8.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/ba/05/6c96328e92e625fc31445d24d75a2c92ef9ba34fc5b037fe69693c362a0d/configparser-3.7.4-py2.py3-none-any.whl\n",
      "Requirement already satisfied: Werkzeug>=0.14 in /usr/local/lib/python3.6/site-packages (from Flask->mlflow->-r requirements.txt (line 1)) (0.15.1)\n",
      "Requirement already satisfied: itsdangerous>=0.24 in /usr/local/lib/python3.6/site-packages (from Flask->mlflow->-r requirements.txt (line 1)) (1.1.0)\n",
      "Requirement already satisfied: Jinja2>=2.10 in /usr/local/lib/python3.6/site-packages (from Flask->mlflow->-r requirements.txt (line 1)) (2.10)\n",
      "Collecting s3transfer<0.3.0,>=0.2.0 (from boto3>=1.7.12->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading https://files.pythonhosted.org/packages/d7/de/5737f602e22073ecbded7a0c590707085e154e32b68d86545dcc31004c02/s3transfer-0.2.0-py2.py3-none-any.whl (69kB)\n",
      "Collecting botocore<1.13.0,>=1.12.130 (from boto3>=1.7.12->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/21/80/6683916e9e1eaf867e09b81a6df6d7a2ba181e3095d2367c7a0f635ed1ed/botocore-1.12.130-py2.py3-none-any.whl (5.4MB)\n",
      "Collecting jmespath<1.0.0,>=0.7.1 (from boto3>=1.7.12->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/83/94/7179c3832a6d45b266ddb2aac329e101367fbdb11f425f13771d27f225bb/jmespath-0.9.4-py2.py3-none-any.whl\n",
      "Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /usr/local/lib/python3.6/site-packages (from requests>=2.17.3->mlflow->-r requirements.txt (line 1)) (3.0.4)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/site-packages (from requests>=2.17.3->mlflow->-r requirements.txt (line 1)) (2019.3.9)\n",
      "Requirement already satisfied: urllib3<1.25,>=1.21.1 in /usr/local/lib/python3.6/site-packages (from requests>=2.17.3->mlflow->-r requirements.txt (line 1)) (1.24.1)\n",
      "Requirement already satisfied: idna<2.9,>=2.5 in /usr/local/lib/python3.6/site-packages (from requests>=2.17.3->mlflow->-r requirements.txt (line 1)) (2.8)\n",
      "Collecting websocket-client>=0.32.0 (from docker>=3.6.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/29/19/44753eab1fdb50770ac69605527e8859468f3c0fd7dc5a76dd9c4dbd7906/websocket_client-0.56.0-py2.py3-none-any.whl (200kB)\n",
      "Collecting docker-pycreds>=0.4.0 (from docker>=3.6.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/f5/e8/f6bd1eee09314e7e6dee49cbe2c5e22314ccdb38db16c9fc72d2fa80d054/docker_pycreds-0.4.0-py2.py3-none-any.whl\n",
      "Requirement already satisfied: setuptools in /usr/local/lib/python3.6/site-packages (from protobuf>=3.6.0->mlflow->-r requirements.txt (line 1)) (40.6.3)\n",
      "Collecting nose (from nose-exclude>=0.5.0->mleap>=0.8.1->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/15/d8/dd071918c040f50fa1cf80da16423af51ff8ce4a0f2399b7bf8de45ac3d9/nose-1.3.7-py3-none-any.whl (154kB)\n",
      "Collecting smmap2>=2.0.0 (from gitdb2>=2.0.0->gitpython>=2.1.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/55/d2/866d45e3a121ee15a1dc013824d58072fd5c7799c9c34d01378eb262ca8f/smmap2-2.0.5-py2.py3-none-any.whl\n",
      "Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.6/site-packages (from Jinja2>=2.10->Flask->mlflow->-r requirements.txt (line 1)) (1.1.1)\n",
      "Collecting docutils>=0.10 (from botocore<1.13.0,>=1.12.130->boto3>=1.7.12->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/36/fa/08e9e6e0e3cbd1d362c3bbee8d01d0aedb2155c4ac112b19ef3cae8eed8d/docutils-0.14-py3-none-any.whl (543kB)\n",
      "Building wheels for collected packages: sklearn, mleap, databricks-cli, querystring-parser, simplejson, nose-exclude, tabulate\n",
      "Building wheel for sklearn (setup.py): started\n",
      "Building wheel for sklearn (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/76/03/bb/589d421d27431bcd2c6da284d5f2286c8e3b2ea3cf1594c074\n",
      "Building wheel for mleap (setup.py): started\n",
      "Building wheel for mleap (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/bc/a5/8d/7d021f2741ed8a7354e4fe19c1890e71ed719d681cd16cf292\n",
      "Building wheel for databricks-cli (setup.py): started\n",
      "Building wheel for databricks-cli (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/7a/3e/18/4770e5048530a759970f03239d88b3d32d7434c410d803cfc9\n",
      "Building wheel for querystring-parser (setup.py): started\n",
      "Building wheel for querystring-parser (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/ee/09/99/bf937e4f02788fa8b33dc5240842ba3977ba5c3c4ad4a115d7\n",
      "Building wheel for simplejson (setup.py): started\n",
      "Building wheel for simplejson (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/5d/1a/1e/0350bb3df3e74215cd91325344cc86c2c691f5306eb4d22c77\n",
      "Building wheel for nose-exclude (setup.py): started\n",
      "Building wheel for nose-exclude (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/fe/b9/8f/764ed47b11e2e062ae8fe5f09d1c801a600292fdd9ba3477bb\n",
      "Building wheel for tabulate (setup.py): started\n",
      "Building wheel for tabulate (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/2b/67/89/414471314a2d15de625d184d8be6d38a03ae1e983dbda91e84\n",
      "Successfully built sklearn mleap databricks-cli querystring-parser simplejson nose-exclude tabulate\n",
      "mlflow 0.9.0.1 has requirement cloudpickle==0.6.1, but you'll have cloudpickle 0.8.1 which is incompatible.\n",
      "Installing collected packages: argparse, scipy, python-dateutil, pytz, pandas, scikit-learn, nose, nose-exclude, mleap, smmap2, gitdb2, gitpython, tabulate, configparser, databricks-cli, querystring-parser, cloudpickle, entrypoints, jmespath, docutils, botocore, s3transfer, boto3, sqlparse, websocket-client, docker-pycreds, docker, simplejson, gunicorn, mlflow, sklearn\n",
      "Successfully installed argparse-1.4.0 boto3-1.9.130 botocore-1.12.130 cloudpickle-0.8.1 configparser-3.7.4 databricks-cli-0.8.6 docker-3.7.2 docker-pycreds-0.4.0 docutils-0.14 entrypoints-0.3 gitdb2-2.0.5 gitpython-2.1.11 gunicorn-19.9.0 jmespath-0.9.4 mleap-0.8.1 mlflow-0.9.0.1 nose-1.3.7 nose-exclude-0.5.0 pandas-0.24.2 python-dateutil-2.8.0 pytz-2019.1 querystring-parser-1.2.3 s3transfer-0.2.0 scikit-learn-0.20.3 scipy-1.2.1 simplejson-3.16.0 sklearn-0.0 smmap2-2.0.5 sqlparse-0.3.0 tabulate-0.8.3 websocket-client-0.56.0\n",
      "Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "You are using pip version 19.0.1, however version 19.0.3 is available.\n",
      "You should consider upgrading via the 'pip install --upgrade pip' command.\n",
      "Build completed successfully\n"
     ]
    }
   ],
   "source": [
    "!s2i build -E environment_rest . seldonio/seldon-core-s2i-python3:0.13 mlflow_model:0.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "44ba8b3fd0999714f4a56f061275973a54564aee4e1161c79e7fbb8288adea9d\r\n"
     ]
    }
   ],
   "source": [
    "!docker run --name \"mlflow_model\" -d --rm -p 5000:5000 mlflow_model:0.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":{\"ndarray\":[5.455573233630147]},\"meta\":{}}\r\n"
     ]
    }
   ],
   "source": [
    "!curl -H \"Content-Type: application/x-www-form-urlencoded\" -g 0.0.0.0:5000/predict -d 'json={\"data\":{\"names\":[\"alcohol\", \"chlorides\", \"citric acid\", \"density\", \"fixed acidity\", \"free sulfur dioxide\", \"pH\", \"residual sugar\", \"sulphates\", \"total sulfur dioxide\", \"volatile acidity\"],\"ndarray\":[[12.8, 0.029, 0.48, 0.98, 6.2, 29, 3.33, 1.2, 0.39, 75, 0.66]]}}'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\"data\":{\"ndarray\":[5.455573233630147]},\"meta\":{}}\r\n"
     ]
    }
   ],
   "source": [
    "!curl -H \"Content-Type: application/x-www-form-urlencoded\" -g 0.0.0.0:5000/predict -d 'json={\"data\":{\"ndarray\":[[12.8, 0.029, 0.48, 0.98, 6.2, 29, 3.33, 1.2, 0.39, 75, 0.66]]}}'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "mlflow_model\r\n"
     ]
    }
   ],
   "source": [
    "!docker rm mlflow_model --force"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test Inference on Minikube\n",
    "\n",
    "**Due to a [minikube/s2i issue](https://github.com/SeldonIO/seldon-core/issues/253) you will need [s2i >= 1.1.13](https://github.com/openshift/source-to-image/releases/tag/v1.1.13)**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "😄  minikube v0.34.1 on linux (amd64)\n",
      "🔥  Creating virtualbox VM (CPUs=2, Memory=4096MB, Disk=20000MB) ...\n",
      "📶  \"minikube\" IP address is 192.168.99.100\n",
      "🐳  Configuring Docker as the container runtime ...\n",
      "✨  Preparing Kubernetes environment ...\n",
      "🚜  Pulling images required by Kubernetes v1.13.3 ...\n",
      "🚀  Launching Kubernetes v1.13.3 using kubeadm ... \n",
      "🔑  Configuring cluster permissions ...\n",
      "🤔  Verifying component health .....\n",
      "💗  kubectl is now configured to use \"minikube\"\n",
      "🏄  Done! Thank you for using minikube!\n"
     ]
    }
   ],
   "source": [
    "!minikube start --memory 4096"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "clusterrolebinding.rbac.authorization.k8s.io/kube-system-cluster-admin created\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl create clusterrolebinding kube-system-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "$HELM_HOME has been configured at /home/clive/.helm.\n",
      "\n",
      "Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.\n",
      "\n",
      "Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.\n",
      "To prevent this, run `helm init` with the --tiller-tls-verify flag.\n",
      "For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation\n",
      "Happy Helming!\n"
     ]
    }
   ],
   "source": [
    "!helm init"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waiting for deployment \"tiller-deploy\" rollout to finish: 0 of 1 updated replicas are available...\n",
      "deployment \"tiller-deploy\" successfully rolled out\n"
     ]
    }
   ],
   "source": [
    "!kubectl rollout status deploy/tiller-deploy -n kube-system"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME:   seldon-core\n",
      "LAST DEPLOYED: Tue Apr 16 14:39:51 2019\n",
      "NAMESPACE: seldon-system\n",
      "STATUS: DEPLOYED\n",
      "\n",
      "RESOURCES:\n",
      "==> v1beta1/ClusterRole\n",
      "NAME                        AGE\n",
      "seldon-spartakus-volunteer  1s\n",
      "\n",
      "==> v1/Pod(related)\n",
      "NAME                                  READY  STATUS             RESTARTS  AGE\n",
      "seldon-operator-controller-manager-0  0/1    ContainerCreating  0         1s\n",
      "\n",
      "==> v1/Secret\n",
      "NAME                                   TYPE    DATA  AGE\n",
      "seldon-operator-webhook-server-secret  Opaque  0     1s\n",
      "\n",
      "==> v1/ConfigMap\n",
      "NAME                     DATA  AGE\n",
      "seldon-spartakus-config  3     1s\n",
      "\n",
      "==> v1beta1/CustomResourceDefinition\n",
      "NAME                                         AGE\n",
      "seldondeployments.machinelearning.seldon.io  1s\n",
      "\n",
      "==> v1/ClusterRole\n",
      "seldon-operator-manager-role  1s\n",
      "\n",
      "==> v1beta1/Deployment\n",
      "NAME                        DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE\n",
      "seldon-spartakus-volunteer  1        0        0           0          1s\n",
      "\n",
      "==> v1/StatefulSet\n",
      "NAME                                DESIRED  CURRENT  AGE\n",
      "seldon-operator-controller-manager  1        1        1s\n",
      "\n",
      "==> v1/ClusterRoleBinding\n",
      "NAME                                 AGE\n",
      "seldon-operator-manager-rolebinding  1s\n",
      "\n",
      "==> v1/Service\n",
      "NAME                                        TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE\n",
      "seldon-operator-controller-manager-service  ClusterIP  10.96.202.141  <none>       443/TCP  1s\n",
      "\n",
      "==> v1/ServiceAccount\n",
      "NAME                        SECRETS  AGE\n",
      "seldon-spartakus-volunteer  1        1s\n",
      "\n",
      "==> v1beta1/ClusterRoleBinding\n",
      "NAME                        AGE\n",
      "seldon-spartakus-volunteer  1s\n",
      "\n",
      "\n",
      "NOTES:\n",
      "NOTES: TODO\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!helm install ../../../helm-charts/seldon-core-operator --name seldon-core --set usageMetrics.enabled=true   --namespace seldon-system"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "partitioned roll out complete: 1 new pods have been updated...\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl rollout status deploy/seldon-controller-manager -n seldon-system"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup Ingress\n",
    "Please note: There are reported gRPC issues with ambassador (see https://github.com/SeldonIO/seldon-core/issues/473)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME:   ambassador\n",
      "LAST DEPLOYED: Tue Apr 16 14:40:42 2019\n",
      "NAMESPACE: seldon\n",
      "STATUS: DEPLOYED\n",
      "\n",
      "RESOURCES:\n",
      "==> v1/Pod(related)\n",
      "NAME                         READY  STATUS             RESTARTS  AGE\n",
      "ambassador-5b89d44544-ph44v  0/1    ContainerCreating  0         0s\n",
      "ambassador-5b89d44544-q4jfn  0/1    ContainerCreating  0         0s\n",
      "ambassador-5b89d44544-qdc9t  0/1    ContainerCreating  0         0s\n",
      "\n",
      "==> v1/ServiceAccount\n",
      "NAME        SECRETS  AGE\n",
      "ambassador  1        1s\n",
      "\n",
      "==> v1beta1/ClusterRole\n",
      "NAME        AGE\n",
      "ambassador  1s\n",
      "\n",
      "==> v1beta1/ClusterRoleBinding\n",
      "NAME        AGE\n",
      "ambassador  1s\n",
      "\n",
      "==> v1/Service\n",
      "NAME               TYPE          CLUSTER-IP      EXTERNAL-IP  PORT(S)                     AGE\n",
      "ambassador-admins  ClusterIP     10.101.35.185   <none>       8877/TCP                    1s\n",
      "ambassador         LoadBalancer  10.108.206.144  <pending>    80:30921/TCP,443:30522/TCP  0s\n",
      "\n",
      "==> v1/Deployment\n",
      "NAME        DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE\n",
      "ambassador  3        3        3           0          0s\n",
      "\n",
      "\n",
      "NOTES:\n",
      "Congratuations! You've successfully installed Ambassador.\n",
      "\n",
      "For help, visit our Slack at https://d6e.co/slack or view the documentation online at https://www.getambassador.io.\n",
      "\n",
      "To get the IP address of Ambassador, run the following commands:\n",
      "NOTE: It may take a few minutes for the LoadBalancer IP to be available.\n",
      "     You can watch the status of by running 'kubectl get svc -w  --namespace seldon ambassador'\n",
      "\n",
      "  On GKE/Azure:\n",
      "  export SERVICE_IP=$(kubectl get svc --namespace seldon ambassador -o jsonpath='{.status.loadBalancer.ingress[0].ip}')\n",
      "\n",
      "  On AWS:\n",
      "  export SERVICE_IP=$(kubectl get svc --namespace seldon ambassador -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')\n",
      "\n",
      "  echo http://$SERVICE_IP:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!helm install stable/ambassador --name ambassador --set crds.keep=false"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waiting for deployment \"ambassador\" rollout to finish: 0 of 3 updated replicas are available...\n",
      "Waiting for deployment \"ambassador\" rollout to finish: 1 of 3 updated replicas are available...\n",
      "Waiting for deployment \"ambassador\" rollout to finish: 2 of 3 updated replicas are available...\n",
      "deployment \"ambassador\" successfully rolled out\n"
     ]
    }
   ],
   "source": [
    "!kubectl rollout status deployment.apps/ambassador"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---> Installing application source...\n",
      "---> Installing dependencies ...\n",
      "Looking in links: /whl\n",
      "Collecting mlflow (from -r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/e5/f7/d349b011f4e03b06a3a0f2916647c578cd89d338f1e244ddb4e1fee600fc/mlflow-0.9.0.1-py3-none-any.whl (11.8MB)\n",
      "Collecting sklearn (from -r requirements.txt (line 2))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/1e/7a/dbb3be0ce9bd5c8b7e3d87328e79063f8b263b2b1bfa4774cb1147bfcd3f/sklearn-0.0.tar.gz\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Collecting pandas (from -r requirements.txt (line 3))\n",
      "Downloading https://files.pythonhosted.org/packages/19/74/e50234bc82c553fecdbd566d8650801e3fe2d6d8c8d940638e3d8a7c5522/pandas-0.24.2-cp36-cp36m-manylinux1_x86_64.whl (10.1MB)\n",
      "Collecting simplejson (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/e3/24/c35fb1c1c315fc0fffe61ea00d3f88e85469004713dab488dee4f35b0aff/simplejson-3.16.0.tar.gz (81kB)\n",
      "Collecting cloudpickle==0.6.1 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/fc/87/7b7ef3038b4783911e3fdecb5c566e3a817ce3e890e164fc174c088edb1e/cloudpickle-0.6.1-py2.py3-none-any.whl\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Collecting pyyaml (from mlflow->-r requirements.txt (line 1))\n",
      "Downloading https://files.pythonhosted.org/packages/9f/2c/9417b5c774792634834e730932745bc09a7d36754ca00acf1ccd1ac2594d/PyYAML-5.1.tar.gz (274kB)\n",
      "Collecting python-dateutil (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/41/17/c62faccbfbd163c7f57f3844689e3a78bae1f403648a6afb1d0866d87fbb/python_dateutil-2.8.0-py2.py3-none-any.whl (226kB)\n",
      "Collecting gitpython>=2.1.0 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/fe/e5/fafe827507644c32d6dc553a1c435cdf882e0c28918a5bab29f7fbebfb70/GitPython-2.1.11-py2.py3-none-any.whl (448kB)\n",
      "Requirement already satisfied: numpy in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (1.16.1)\n",
      "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (1.12.0)\n",
      "Collecting docker>=3.6.0 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/48/68/c3afca1a5aa8d2997ec3b8ee822a4d752cf85907b321f07ea86888545152/docker-3.7.2-py2.py3-none-any.whl (134kB)\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Collecting gunicorn (from mlflow->-r requirements.txt (line 1))\n",
      "Downloading https://files.pythonhosted.org/packages/8c/da/b8dd8deb741bff556db53902d4706774c8e1e67265f69528c14c003644e6/gunicorn-19.9.0-py2.py3-none-any.whl (112kB)\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Collecting databricks-cli>=0.8.0 (from mlflow->-r requirements.txt (line 1))\n",
      "Downloading https://files.pythonhosted.org/packages/b2/9e/e2868a5560827357796f8682803bbac73d30de4e890604e61f2488ce9c03/databricks-cli-0.8.6.tar.gz (41kB)\n",
      "Collecting scipy (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/7f/5f/c48860704092933bf1c4c1574a8de1ffd16bf4fde8bab190d747598844b2/scipy-1.2.1-cp36-cp36m-manylinux1_x86_64.whl (24.8MB)\n",
      "Requirement already satisfied: Flask in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (1.0.2)\n",
      "Collecting entrypoints (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/ac/c6/44694103f8c221443ee6b0041f69e2740d89a25641e62fb4f2ee568f2f9c/entrypoints-0.3-py2.py3-none-any.whl\n",
      "Collecting querystring-parser (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/57/64/3086a9a991ff3aca7b769f5b0b51ff8445a06337ae2c58f215bcee48f527/querystring_parser-1.2.3.tar.gz\n",
      "Collecting mleap>=0.8.1 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/3b/17/486a3cf30695581f029ba618ee878252a9e9f859c48913e30a07d13c8a21/mleap-0.8.1.tar.gz\n",
      "Collecting scikit-learn (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/5e/82/c0de5839d613b82bddd088599ac0bbfbbbcbd8ca470680658352d2c435bd/scikit_learn-0.20.3-cp36-cp36m-manylinux1_x86_64.whl (5.4MB)\n",
      "Collecting boto3>=1.7.12 (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/c3/ef/f81d8c35c7254fe2af9a1a8e034f07b88a824a441f37955d0a07a90b8ec7/boto3-1.9.130-py2.py3-none-any.whl (128kB)\n",
      "Requirement already satisfied: protobuf>=3.6.0 in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (3.6.1)\n",
      "Requirement already satisfied: requests>=2.17.3 in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (2.21.0)\n",
      "Requirement already satisfied: click>=7.0 in /usr/local/lib/python3.6/site-packages (from mlflow->-r requirements.txt (line 1)) (7.0)\n",
      "Collecting sqlparse (from mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/ef/53/900f7d2a54557c6a37886585a91336520e5539e3ae2423ff1102daf4f3a7/sqlparse-0.3.0-py2.py3-none-any.whl\n",
      "Collecting pytz>=2011k (from pandas->-r requirements.txt (line 3))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/3d/73/fe30c2daaaa0713420d0382b16fbb761409f532c56bdcc514bf7b6262bb6/pytz-2019.1-py2.py3-none-any.whl (510kB)\n",
      "Collecting gitdb2>=2.0.0 (from gitpython>=2.1.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/da/30/a407568aa8d8f25db817cf50121a958722f3fc5f87e3a6fba1f40c0633e3/gitdb2-2.0.5-py2.py3-none-any.whl (62kB)\n",
      "Collecting websocket-client>=0.32.0 (from docker>=3.6.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/29/19/44753eab1fdb50770ac69605527e8859468f3c0fd7dc5a76dd9c4dbd7906/websocket_client-0.56.0-py2.py3-none-any.whl (200kB)\n",
      "Collecting docker-pycreds>=0.4.0 (from docker>=3.6.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/f5/e8/f6bd1eee09314e7e6dee49cbe2c5e22314ccdb38db16c9fc72d2fa80d054/docker_pycreds-0.4.0-py2.py3-none-any.whl\n",
      "Collecting tabulate>=0.7.7 (from databricks-cli>=0.8.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/c2/fd/202954b3f0eb896c53b7b6f07390851b1fd2ca84aa95880d7ae4f434c4ac/tabulate-0.8.3.tar.gz (46kB)\n",
      "Collecting configparser>=0.3.5 (from databricks-cli>=0.8.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading https://files.pythonhosted.org/packages/ba/05/6c96328e92e625fc31445d24d75a2c92ef9ba34fc5b037fe69693c362a0d/configparser-3.7.4-py2.py3-none-any.whl\n",
      "Requirement already satisfied: Werkzeug>=0.14 in /usr/local/lib/python3.6/site-packages (from Flask->mlflow->-r requirements.txt (line 1)) (0.14.1)\n",
      "Requirement already satisfied: Jinja2>=2.10 in /usr/local/lib/python3.6/site-packages (from Flask->mlflow->-r requirements.txt (line 1)) (2.10)\n",
      "Requirement already satisfied: itsdangerous>=0.24 in /usr/local/lib/python3.6/site-packages (from Flask->mlflow->-r requirements.txt (line 1)) (1.1.0)\n",
      "Collecting argparse>=1.1 (from mleap>=0.8.1->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/f2/94/3af39d34be01a24a6e65433d19e107099374224905f1e0cc6bbe1fd22a2f/argparse-1.4.0-py2.py3-none-any.whl\n",
      "Collecting nose-exclude>=0.5.0 (from mleap>=0.8.1->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/63/cf/90c4be56bf11b7bc8801086d9445baf731aa36b8e8fc5791731e8e604dcd/nose-exclude-0.5.0.tar.gz\n",
      "Collecting s3transfer<0.3.0,>=0.2.0 (from boto3>=1.7.12->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/d7/de/5737f602e22073ecbded7a0c590707085e154e32b68d86545dcc31004c02/s3transfer-0.2.0-py2.py3-none-any.whl (69kB)\n",
      "Collecting jmespath<1.0.0,>=0.7.1 (from boto3>=1.7.12->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/83/94/7179c3832a6d45b266ddb2aac329e101367fbdb11f425f13771d27f225bb/jmespath-0.9.4-py2.py3-none-any.whl\n",
      "Collecting botocore<1.13.0,>=1.12.130 (from boto3>=1.7.12->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/21/80/6683916e9e1eaf867e09b81a6df6d7a2ba181e3095d2367c7a0f635ed1ed/botocore-1.12.130-py2.py3-none-any.whl (5.4MB)\n",
      "Requirement already satisfied: setuptools in /usr/local/lib/python3.6/site-packages (from protobuf>=3.6.0->mlflow->-r requirements.txt (line 1)) (40.8.0)\n",
      "Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /usr/local/lib/python3.6/site-packages (from requests>=2.17.3->mlflow->-r requirements.txt (line 1)) (3.0.4)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/site-packages (from requests>=2.17.3->mlflow->-r requirements.txt (line 1)) (2018.11.29)\n",
      "Requirement already satisfied: urllib3<1.25,>=1.21.1 in /usr/local/lib/python3.6/site-packages (from requests>=2.17.3->mlflow->-r requirements.txt (line 1)) (1.24.1)\n",
      "Requirement already satisfied: idna<2.9,>=2.5 in /usr/local/lib/python3.6/site-packages (from requests>=2.17.3->mlflow->-r requirements.txt (line 1)) (2.8)\n",
      "Collecting smmap2>=2.0.0 (from gitdb2>=2.0.0->gitpython>=2.1.0->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/55/d2/866d45e3a121ee15a1dc013824d58072fd5c7799c9c34d01378eb262ca8f/smmap2-2.0.5-py2.py3-none-any.whl\n",
      "Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.6/site-packages (from Jinja2>=2.10->Flask->mlflow->-r requirements.txt (line 1)) (1.1.0)\n",
      "Collecting nose (from nose-exclude>=0.5.0->mleap>=0.8.1->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/15/d8/dd071918c040f50fa1cf80da16423af51ff8ce4a0f2399b7bf8de45ac3d9/nose-1.3.7-py3-none-any.whl (154kB)\n",
      "Collecting docutils>=0.10 (from botocore<1.13.0,>=1.12.130->boto3>=1.7.12->mlflow->-r requirements.txt (line 1))\n",
      "  Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/36/fa/08e9e6e0e3cbd1d362c3bbee8d01d0aedb2155c4ac112b19ef3cae8eed8d/docutils-0.14-py3-none-any.whl (543kB)\n",
      "Building wheels for collected packages: sklearn, simplejson, pyyaml, databricks-cli, querystring-parser, mleap, tabulate, nose-exclude\n",
      "Building wheel for sklearn (setup.py): started\n",
      "Building wheel for sklearn (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/76/03/bb/589d421d27431bcd2c6da284d5f2286c8e3b2ea3cf1594c074\n",
      "Building wheel for simplejson (setup.py): started\n",
      "Building wheel for simplejson (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/5d/1a/1e/0350bb3df3e74215cd91325344cc86c2c691f5306eb4d22c77\n",
      "Building wheel for pyyaml (setup.py): started\n",
      "Building wheel for pyyaml (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/ad/56/bc/1522f864feb2a358ea6f1a92b4798d69ac783a28e80567a18b\n",
      "Building wheel for databricks-cli (setup.py): started\n",
      "Building wheel for databricks-cli (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/7a/3e/18/4770e5048530a759970f03239d88b3d32d7434c410d803cfc9\n",
      "Building wheel for querystring-parser (setup.py): started\n",
      "Building wheel for querystring-parser (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/ee/09/99/bf937e4f02788fa8b33dc5240842ba3977ba5c3c4ad4a115d7\n",
      "Building wheel for mleap (setup.py): started\n",
      "Building wheel for mleap (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/bc/a5/8d/7d021f2741ed8a7354e4fe19c1890e71ed719d681cd16cf292\n",
      "Building wheel for tabulate (setup.py): started\n",
      "Building wheel for tabulate (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/2b/67/89/414471314a2d15de625d184d8be6d38a03ae1e983dbda91e84\n",
      "Building wheel for nose-exclude (setup.py): started\n",
      "Building wheel for nose-exclude (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/fe/b9/8f/764ed47b11e2e062ae8fe5f09d1c801a600292fdd9ba3477bb\n",
      "Successfully built sklearn simplejson pyyaml databricks-cli querystring-parser mleap tabulate nose-exclude\n",
      "Installing collected packages: simplejson, cloudpickle, pyyaml, python-dateutil, smmap2, gitdb2, gitpython, websocket-client, docker-pycreds, docker, pytz, pandas, gunicorn, tabulate, configparser, databricks-cli, scipy, entrypoints, querystring-parser, argparse, scikit-learn, nose, nose-exclude, mleap, jmespath, docutils, botocore, s3transfer, boto3, sqlparse, mlflow, sklearn\n",
      "Successfully installed argparse-1.4.0 boto3-1.9.130 botocore-1.12.130 cloudpickle-0.6.1 configparser-3.7.4 databricks-cli-0.8.6 docker-3.7.2 docker-pycreds-0.4.0 docutils-0.14 entrypoints-0.3 gitdb2-2.0.5 gitpython-2.1.11 gunicorn-19.9.0 jmespath-0.9.4 mleap-0.8.1 mlflow-0.9.0.1 nose-1.3.7 nose-exclude-0.5.0 pandas-0.24.2 python-dateutil-2.8.0 pytz-2019.1 pyyaml-5.1 querystring-parser-1.2.3 s3transfer-0.2.0 scikit-learn-0.20.3 scipy-1.2.1 simplejson-3.16.0 sklearn-0.0 smmap2-2.0.5 sqlparse-0.3.0 tabulate-0.8.3 websocket-client-0.56.0\n",
      "Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Build completed successfully\n"
     ]
    }
   ],
   "source": [
    "!eval $(minikube docker-env) && s2i build -E environment_rest . seldonio/seldon-core-s2i-python3:0.13 mlflow_model:0.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "seldondeployment.machinelearning.seldon.io/mlflow-example created\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl create -f deployment.json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waiting for deployment \"mlflow-dep-mlflow-pred-d580056\" rollout to finish: 0 of 1 updated replicas are available...\n",
      "deployment \"mlflow-dep-mlflow-pred-d580056\" successfully rolled out\n"
     ]
    }
   ],
   "source": [
    "!kubectl rollout status deployment/mlflow-dep-mlflow-pred-d580056"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------\n",
      "SENDING NEW REQUEST:\n",
      "\n",
      "[[2.8040e+00 7.5600e-01 2.8000e-02 1.3500e-01 5.1000e-02 6.6000e-02\n",
      "  6.8270e+00 8.6900e-01 2.0100e-01 7.5268e+01 4.2500e-01]]\n",
      "RECEIVED RESPONSE:\n",
      "meta {\n",
      "  puid: \"fjmht64oou6r5f8b8ju3aagu4u\"\n",
      "  requestPath {\n",
      "    key: \"model\"\n",
      "    value: \"mlflow_model:0.1\"\n",
      "  }\n",
      "}\n",
      "data {\n",
      "  ndarray {\n",
      "    values {\n",
      "      number_value: 5.1800195352034475\n",
      "    }\n",
      "  }\n",
      "}\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!seldon-core-api-tester contract.json `minikube ip` `kubectl get svc ambassador -o jsonpath='{.spec.ports[0].nodePort}'` \\\n",
    "    mlflow-example --namespace seldon -p"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!minikube delete"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
